Skip to content

Add history heuristic for quiet move ordering#53

Open
luccabb wants to merge 1 commit intomasterfrom
improve/history-heuristic
Open

Add history heuristic for quiet move ordering#53
luccabb wants to merge 1 commit intomasterfrom
improve/history-heuristic

Conversation

@luccabb
Copy link
Owner

@luccabb luccabb commented Feb 16, 2026

Summary

Track which quiet moves (from_square → to_square) cause beta cutoffs across the search tree using a 64×64 history table. Quiet moves with higher history scores are searched first, dramatically improving alpha-beta cutoff rates.

  • History table: 64×64 integer array (from_square, to_square) initialized to zero at search start
  • Update rule: On beta cutoff for a quiet move, increment by depth² (deeper cutoffs weighted more)
  • Move ordering: Quiet moves sorted by history score descending (replaces random shuffle)
  • Backward compatible: organize_moves() accepts optional history parameter; without it, behavior is unchanged

Benchmark results (depth 4)

Metric Before After Change
Total nodes 4,760,507 3,124,410 −34.4%
NPS 22,634 24,696 +9.1%
Total time 210.32s 126.51s −39.8%

Notable per-position improvements:

  • Position 46: 986,591 → 20,495 (−97.9%)
  • Position 1: 16,405 → 6,060 (−63.1%)
  • Position 8: 134,526 → 51,062 (−62.1%)

Local Stockfish Benchmark

Settings: 20 games, Stockfish skill 3, 10s/move, no opening book.

W L D Win Rate
Master (baseline) 19 1 0 95%
This PR 20 0 0 100%

Use /run-stockfish-benchmark for CI validation with opening book and longer time control.

Test plan

  • All alpha_beta unit tests pass (16/16)
  • /run-nps-benchmark
  • /run-stockfish-benchmark

Track which quiet moves (from_square → to_square) cause beta cutoffs
across the search tree using a 64×64 history table. Quiet moves with
higher history scores are searched first, dramatically improving
alpha-beta cutoff rates.

On beta cutoff of a quiet move, the history score is incremented by
depth² (deeper cutoffs are weighted more heavily).

Benchmark at depth 4:
- Nodes: 4,760,507 → 3,124,410 (−34.4%)
- NPS: 22,634 → 24,696 (+9.1%)
- Total time: 210.32s → 126.51s (−39.8%)

Some positions show enormous reductions, e.g., Position 46:
986,591 → 20,495 nodes (−97.9%).
@github-actions
Copy link

Benchmarks

The following benchmarks are available for this PR:

Command Description
/run-nps-benchmark NPS speed benchmark (depth 5, 48 positions)
/run-stockfish-benchmark Stockfish strength benchmark (300 games)

Post a comment with the command to trigger a benchmark run.

@greptile-apps
Copy link

greptile-apps bot commented Feb 16, 2026

Greptile Summary

Implements history heuristic for quiet move ordering using a 64×64 table indexed by (from_square, to_square). Quiet moves that cause beta cutoffs increment their history score by depth², and subsequent searches prioritize high-scoring moves. This dramatically improves alpha-beta pruning efficiency (−34.4% nodes, −39.8% search time at depth 4).

Key changes:

  • Added new_history_table() function and HISTORY_TYPE alias in alpha_beta.py
  • History table created per search in search_move() and passed through all negamax() calls
  • Beta cutoffs for quiet moves increment history[from_square][to_square] += depth * depth
  • organize_moves() now sorts quiet moves by history score descending (when history provided)
  • Backward compatible: parallel engines (LazySMP, layer-based) will use history=None and maintain existing behavior

Architecture:

  • History table is per-search (not shared across parallel processes), avoiding race conditions
  • Only quiet moves tracked (captures excluded via board.is_capture() check)
  • Depth-squared weighting ensures deeper cutoffs have more influence

Confidence Score: 5/5

  • This PR is safe to merge - well-tested optimization with clean implementation
  • Score reflects a clean, focused implementation of a standard chess optimization technique. The history heuristic is correctly implemented (depth² weighting, quiet-only, beta-cutoff triggered), properly threaded through recursion, and backward compatible. No race conditions (per-search table), no logic errors, and impressive benchmark results validate the approach. All existing tests pass.
  • No files require special attention

Important Files Changed

Filename Overview
moonfish/engines/alpha_beta.py Added history heuristic with 64×64 table tracking quiet moves causing beta cutoffs, properly threaded through negamax recursion
moonfish/move_ordering.py Updated organize_moves() to sort quiet moves by history score when provided, maintains backward compatibility

Flowchart

flowchart TD
    A[search_move: Initialize history table 64×64] --> B[Call negamax with history]
    B --> C{Depth > 0?}
    C -->|No| D[Quiescence search]
    C -->|Yes| E[organize_moves with history]
    E --> F[Sort: captures first, then quiet moves by history score]
    F --> G[Loop through moves]
    G --> H[Recursive negamax with history]
    H --> I{Score >= beta?}
    I -->|Yes - Beta cutoff| J{Is quiet move?}
    J -->|Yes| K[history from_sq to_sq += depth²]
    J -->|No - Capture| L[Skip history update]
    K --> M[Return beta cutoff]
    L --> M
    I -->|No| N[Update alpha, continue loop]
    N --> G
    D --> O[Return evaluation]
    M --> O
Loading

Last reviewed commit: b85883e

Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 files reviewed, no comments

Edit Code Review Agent Settings | Greptile

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant